package org.lisy.java.reflect;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Properties;

/**
 * 反射操作
 *
 * @author lisy
 */
public class ReflectOperate {

	/**
	 * 反射:
	 *   反射机制指的是程序在运行时能够获取自身的信息。java 中，只要给定类的名字，那么就可以通过
	 * 反射机制来获得类的所有属性和方法
	 * 作用:
	 *   1. 在运行时判断任意一个对象所属的类
	 *   2. 在运行时判断任意一个类所具有的成员变量和方法
	 *   3. 在运行时任意调用一个对象的方法
	 *   4. 在运行时构造任意一个类的对象
	 * Class 类:
	 *   Java.lang.Class 是一个比较特殊的类，它用于封装被装入到 JVM 中的类（包括类和接口）的信息。
	 * 当一个类或接口被装入的 JVM 时便会产生一个与之关联的 java.lang.Class 对象，可以通过这个 Class
	 * 对象对被装入类的详细信息进行访问
	 *
	 * 静态编译: 在编译时确定类型，绑定对象，即通过
	 * 动态编译: 运行时确定类型，绑定对象。动态编译最大限度发挥了 Java 的灵活性，体现了多态的应用，有以降低类之间的藕合性
	 *
	 * 反射优缺点
	 * 优点: 可以实现动态创建对象和编译，体现出很大的灵活性
	 * 缺点: 对性能有影响
	 */
	public static void main(String[] args) {
		// 反射创建方式
		testReflect();

		// 通过文件动态创建
		Properties pro = Init.getPro();
		IFruit f = Factory.getInstance(pro.getProperty("apple"));
        if (f != null){
            f.eat();
        }

        // 反射获取属性值
		User user = new User();
        try {
			Class<? extends User> clazz = user.getClass();
			Field fieldName = clazz.getDeclaredField("name");
			fieldName.setAccessible(true);
			System.out.println(fieldName.get(user));
			Field fieldAge = clazz.getDeclaredField("age");
			fieldAge.setAccessible(true);
			System.out.println(fieldAge.get(user));
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

	private static void testReflect() {
		// 以String为例：
		String str = "hello world";
		// 方式一 ：通过对象的getClass()方法
		Class<?> clazz1 = str.getClass();
		System.out.println(clazz1.getName());
		// 方式二 ：通过对象的getSuperClass()方法
		Class<?> clazz2 = clazz1.getClass();
		System.out.println(clazz2.getSuperclass().getName());
		// 方式三：通过类的class属性
		Class<?> clazz3 = String.class;
		System.out.println(clazz3.getName());
		// 方式四：通过Class类的静态方法forName(String className)
		try {
			Class<?> clazz4 = Class.forName("java.lang.String");
			System.out.println(clazz4.getName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		// 方式五：通过基本类型的包装类.TYPE
		Class<?> clazz5 = Long.TYPE;
		System.out.println(clazz5.getName());
		System.out.println("==============");

		System.out.println("1、通过类本身获得对象");
		Class<?> clazz = str.getClass();
		System.out.println(clazz.getName());
		System.out.println("==============");

		System.out.println("2、通过子类的实例获得父类对象");
		Apple appale = new Apple();
		clazz = appale.getClass();
		System.out.println(appale.getClass().getName());
		System.out.println(clazz.getSuperclass().getName());
		System.out.println("==============");
	}
}

/**
 * 操作属性文件类
 */
class Init {
	public static Properties getPro() {
		Properties pro = new Properties();
		try {
			File f = new File("fruit.properties");
			if (f.exists()) {
				pro.load(new FileInputStream(f));
			} else {
				pro.setProperty("apple", "org.lisy.java.reflect.Apple");
				pro.setProperty("orange", "org.lisy.java.reflect.Orange");
				pro.store(new FileOutputStream(f), "FRUIT CLASS");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return pro;
	}
}

class Factory {
	public static IFruit getInstance(String ClassName) {

		IFruit fruit = null;
		try {
			fruit = (IFruit) Class.forName(ClassName).getDeclaredConstructor().newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return fruit;
	}
}

interface IFruit {
	public abstract void eat();
}

class Apple implements IFruit {
	public void eat() {
		System.out.println("Apple");
	}
}

class Orange implements IFruit {
	public void eat() {
		System.out.println("Orange");
	}
}

class User {
    private String name = "li";
    private int age = 30;
}
